using System;
using System.Collections.Generic;
using System.Text;
using RideMe.Core.Properties;
using System.Diagnostics;
using System.IO;

namespace RideMe.Core
{
    /// <summary>
    /// Used as the subject in an observer design pattern. 
    /// Whenever a new project is opened, or files are
    /// modified, the project service fires events so 
    /// the various observes can take appropriate action. 
    /// </summary>
    public static class ProjectService
    {
        private static List<string> _openFiles = new List<string>();

        public static List<string> OpenFiles
        {
            get { return _openFiles; }
        }

        private const int RecentProjectLimit = 10;
        private static ProjectProcess _newProject;
        private static WebServerProcess _webServer;
        private static Testing _testing;
        private static RakeProcess _rakeProc;
        private static string _rakeOutput;

        // The project path should only change after a project is opened, or a new
        // project has been created. Do you understand?
        public static event EventHandler<ProjectPathChangedEventArgs> ProjectPathChanged;
        public static event EventHandler<ProjectFileEventArgs> FileOpened;
        public static event EventHandler<ProjectFileEventArgs> FileClosed;
        public static event EventHandler ProjectClosed;
        public static event EventHandler SaveClicked;
        public static event EventHandler SaveAllClicked;
		public static event EventHandler ProjectStarted;
		public static event EventHandler ProjectStopped;
        public static event EventHandler<FileRenameEventArgs> SaveAsClicked;
        public static event EventHandler<ProjectFileEventArgs> FileDeleted;
        public static event EventHandler<ProjectFileEventArgs> OpenFileSelected;
        public static event EventHandler<DataReceivedEventArgs> OutputReceived;
        public static event EventHandler<FileRenameEventArgs> FileRenamed;
        public static event EventHandler TaskCompleted;
        public static event EventHandler<ProjectPathChangedEventArgs> RecentProjectAdded;
        public static event EventHandler<CaretPositionChangedEventArgs> EditorCaretPositionChanged;
        public static event EventHandler LastDocumentClosed;
        public static event EventHandler WebServerStarted;
		public static event EventHandler WebServerStarting;
        public static event EventHandler<ProjectPathChangedEventArgs> RecentProjectRemoved;
        public static event EventHandler<SelectedFileChangedEventArgs> SelectedFileChanged;
        public static event EventHandler<ProjectFileEventArgs> TabChanged;
        public static event EventHandler<ProjectFileEventArgs> PreparedForServer;
        public static event EventHandler<RakeEventArgs> RakeOutputReceived;
        public static event EventHandler<ProjectLoadedEventArgs> ProjectLoaded;
		public static event EventHandler<LogFileWatcher.RailsLogFileEventArgs> LogErrorFound;
        public static event EventHandler<DataReceivedEventArgs> ErrorRecieved;

        /// <summary>
        /// Raises the project path changed event.
        /// </summary>
        /// <param name="e">The <see cref="T:RideMe.Core.ProjectPathChangedEventArgs"/> instance containing the event data.</param>
        private static void OnProjectPathChanged(ProjectPathChangedEventArgs e)
        {
            if (ProjectPathChanged != null)
            {
                ProjectPathChanged(null, e);
            }
        }

		/// <summary>
		/// Raises the browser started event.
		/// </summary>
		/// <param name="e">The <see cref="T:RideMe.Core.ProjectFileEventArgs"/> instance containing the event data.</param>
		private static void OnProjectStarted()
		{
			_projectRunning = true;

			if (ProjectStarted != null)
			{
				ProjectStarted(null, EventArgs.Empty);
			}
		}

        /// <summary>
        /// Raises the file opened event.
        /// </summary>
        /// <param name="e">The <see cref="T:RideMe.Core.ProjectFileEventArgs"/> instance containing the event data.</param>
        private static void OnFileOpened(ProjectFileEventArgs e)
        {
            if (FileOpened != null)
            {
                FileOpened(null, e);
            }
        }

        /// <summary>
        /// Raises the file closed event.
        /// </summary>
        /// <param name="e">The <see cref="T:RideMe.Core.ProjectFileEventArgs"/> instance containing the event data.</param>
        private static void OnFileClosed(ProjectFileEventArgs e)
        {
            if (FileClosed != null)
            {
                FileClosed(null, e);
            }
        }

        /// <summary>
        /// Raises the project closed event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private static void OnProjectClosed(EventArgs e)
        {
            if (ProjectClosed != null)
            {
                Options.Instance.LastLoadedProjectPath = string.Empty;
                ProjectClosed(null, e);
            }
        }

        /// <summary>
        /// Raises the save clicked event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private static void OnSaveClicked(EventArgs e)
        {
            if (SaveClicked != null)
            {
                SaveClicked(null, e);
            }
        }

        /// <summary>
        /// Raises the save all clicked event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private static void OnSaveAllClicked(EventArgs e)
        {
            if (SaveAllClicked != null)
            {
                SaveAllClicked(null, e);
            }
        }

        /// <summary>
        /// Raises the file deleted event.
        /// </summary>
        /// <param name="e">The <see cref="T:RideMe.Core.ProjectFileEventArgs"/> instance containing the event data.</param>
        private static void OnFileDeleted(ProjectFileEventArgs e)
        {
            if (FileDeleted != null)
            {
                FileDeleted(null, e);
            }
        }

        /// <summary>
        /// Raises the open file selected event.
        /// </summary>
        /// <param name="e">The <see cref="T:RideMe.Core.ProjectFileEventArgs"/> instance containing the event data.</param>
        private static void OnOpenFileSelected(ProjectFileEventArgs e)
        {
            if (OpenFileSelected != null)
            {
                OpenFileSelected(null, e);
            }
        }

        /// <summary>
        /// Raises the output received event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.Diagnostics.DataReceivedEventArgs"/> instance containing the event data.</param>
        private static void OnOutputReceived(DataReceivedEventArgs e)
        {
            if (OutputReceived != null)
            {
                OutputReceived(null, e);
            }
        }

        /// <summary>
        /// Raises the file renamed event.
        /// </summary>
        /// <param name="e">The <see cref="T:RideMe.Core.FileRenameEventArgs"/> instance containing the event data.</param>
        private static void OnFileRenamed(FileRenameEventArgs e)
        {
            if (FileRenamed != null)
            {
                FileRenamed(null, e);
            }
        }

        /// <summary>
        /// Raises the generating complete event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private static void OnTaskCompleted(EventArgs e)
        {
            if (TaskCompleted != null)
            {
                TaskCompleted(null, e);
            }
        }

        /// <summary>
        /// Raises the recent project added event.
        /// </summary>
        /// <param name="e">The <see cref="T:RideMe.Core.ProjectPathChangedEventArgs"/> instance containing the event data.</param>
        private static void OnRecentProjectAdded(ProjectPathChangedEventArgs e)
        {
            if (RecentProjectAdded != null)
            {
                RecentProjectAdded(null, e);
            }
        }

        /// <summary>
        /// Raises the recent project removed event.
        /// </summary>
        /// <param name="e">The <see cref="T:RideMe.Core.ProjectPathChangedEventArgs"/> instance containing the event data.</param>
        private static void OnRecentProjectRemoved(ProjectPathChangedEventArgs e)
        {
            if (RecentProjectRemoved != null)
            {
                RecentProjectRemoved(null, e);
            }
        }

        /// <summary>
        /// Raises the editor caret position changed event.
        /// </summary>
        /// <param name="e">The <see cref="T:RideMe.Core.CaretPositionChangedEventArgs"/> instance containing the event data.</param>
        private static void OnEditorCaretPositionChanged(CaretPositionChangedEventArgs e)
        {
            if (EditorCaretPositionChanged != null)
            {
                EditorCaretPositionChanged(null, e);
            }
        }

        /// <summary>
        /// Raises the last document closed event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private static void OnLastDocumentClosed(EventArgs e)
        {
            if (LastDocumentClosed != null)
            {
                LastDocumentClosed(null, e);
            }
        }

        /// <summary>
        /// Raises the web server started event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private static void OnWebServerStarted(EventArgs e)
        {
            if (WebServerStarted != null)
            {
                WebServerStarted(null, e);
            }
        }

        /// <summary>
        /// Raises the web server starting event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private static void OnWebServerStarting(EventArgs e)
        {
            if (WebServerStarting != null)
            {
                WebServerStarting(null, e);
            }
        }

        /// <summary>
        /// Raises the SelectedFileChanged event.
        /// </summary>
        /// <param name="e"></param>
        private static void OnSelectedFileChanged(SelectedFileChangedEventArgs e)
        {
            if (SelectedFileChanged != null)
            {
                SelectedFileChanged(null, e);
            }
        }

        /// <summary>
        /// Raises the TabChanged event.
        /// </summary>
        /// <param name="e"></param>
        private static void OnTabChanged(ProjectFileEventArgs e)
        {
            if (TabChanged != null)
            {
                TabChanged(null, e);
            }
        }

        /// <summary>
        /// Raises the SaveAs clicked event.
        /// </summary>
        /// <param name="e"></param>
        private static void OnSaveAsClicked(FileRenameEventArgs e)
        {
            if (SaveAsClicked != null)
            {
                SaveAsClicked(null, e);
            }
        }

        /// <summary>
        /// Raises the PreparedForServer event.
        /// </summary>
        /// <param name="e"></param>
        private static void OnPreparedForSever(ProjectFileEventArgs e)
        {
            if (PreparedForServer != null)
            {
                PreparedForServer(null, e);
            }
        }

        /// <summary>
        /// Raises the StatsReceived event.
        /// </summary>
        /// <param name="e"></param>
        private static void OnRakeOutputRecived(RakeEventArgs e)
        {
            if (RakeOutputReceived != null)
            {
                RakeOutputReceived(null, e);
            }
        }

        /// <summary>
        /// Raises the ProjectLoaded event.
        /// </summary>
        /// <param name="e"></param>
        private static void OnProjectLoaded(ProjectLoadedEventArgs e)
        {
            if (ProjectLoaded != null)
            {
                ProjectLoaded(null, e);
            }
        }

        private static void OnErrorRecieved(DataReceivedEventArgs e)
        {
            if (ErrorRecieved != null)
            {
                ErrorRecieved(null, e);
            }
        }

        /// <summary>
        /// Gets the number of open files.
        /// </summary>
        /// <value>The number of open files.</value>
        public static int NumberOfOpenFiles
        {
            get { return _openFiles.Count; }
        }

        private static string _projectPath;

        /// <summary>
        /// Gets or sets the project path.
        /// </summary>
        /// <value>The project path.</value>
        public static string ProjectPath
        {
            get { return _projectPath; }
            set 
            {
                if (_projectPath != value)
                {
                    if (Directory.Exists(value))
                    {
                        OnProjectPathChanged(new ProjectPathChangedEventArgs(_projectPath, value));

                        _projectPath = value;

                        AddRecentProject(_projectPath);

                        Options.Instance.LastLoadedProjectPath = _projectPath;
                    }
                    else
                    {
                        //throw new ArgumentException();
                    }
                }
            }
        }

        private static List<string> _recentProjects;

        /// <summary>
        /// Gets the recent projects.
        /// </summary>
        /// <value>The recent projects.</value>
        public static IList<string> RecentProjects
        {
            get
            {
                return _recentProjects;
            }
        }

        /// <summary>
        /// Removes the recent project.
        /// </summary>
        /// <param name="path">The path.</param>
        public static void RemoveRecentProject(string path)
        {
            _recentProjects.Remove(path);
            OnRecentProjectRemoved(new ProjectPathChangedEventArgs(path, path));
        }

        /// <summary>
        /// Gets a value indicating whether [project open].
        /// </summary>
        /// <value><c>true</c> if [project open]; otherwise, <c>false</c>.</value>
        public static bool ProjectOpen
        {
            get { return (_projectPath != null) && (_projectPath.Trim().Length > 0); }
        }

        private static bool _projectRunning;

        /// <summary>
        /// Gets or sets a value indicating whether [project running].
        /// </summary>
        /// <value><c>true</c> if [project running]; otherwise, <c>false</c>.</value>
        public static bool ProjectRunning
        {
            get { return _projectRunning; }
            set { _projectRunning = value; }
        }

        /// <summary>
        /// Adds the open file.
        /// </summary>
        /// <param name="file">The file.</param>
        public static void AddOpenFile(string file)
        {
            if (!Validator.ValidFile(file)) return;
            

            // Check if the file is already open. If so fire an event.
            // so the tab can be selected in the document explorer.
            if (_openFiles.Contains(file))
            {
                OnOpenFileSelected(new ProjectFileEventArgs(file));
                return;
            }
            
            _openFiles.Add(file);

            OnFileOpened(new ProjectFileEventArgs(file));
        }

        public static void AddOpenFile(string file, int offset, int length)
        {
            ProjectFileEventArgs e = new ProjectFileEventArgs(file);
            e.Offset = offset;
            e.Length = length;

            // Check if the file is already open. If so fire an event.
            // so the tab can be selected in the document explorer.
            if (_openFiles.Contains(file))
            {
                OnOpenFileSelected(e);
                return;
            }

            _openFiles.Add(file);

            OnFileOpened(e);

        }

        /// <summary>
        /// Closes the file.
        /// </summary>
        /// <param name="file">The file.</param>
        public static void CloseFile(string file)
        {
            _openFiles.Remove(file);

            OnFileClosed(new ProjectFileEventArgs(file));
        }

        /// <summary>
        /// Closes the current project.
        /// </summary>
        public static void CloseCurrentProject()
        {
            _projectPath = string.Empty;
            _openFiles.Clear();
            OnProjectClosed(EventArgs.Empty);

            if (_projectRunning)
            {
                StopProject();
            }

			if (logWatcher != null)
			{
				logWatcher.Dispose();
				logWatcher = null;
			}
        }

        /// <summary>
        /// Saves this instance.
        /// </summary>
        public static void Save()
        {
            OnSaveClicked(EventArgs.Empty);
        }

        public static void SaveAs(string oldFilePath, string newFilePath)
        {
            RenameOpenFile(oldFilePath, newFilePath);
            OnSaveAsClicked(new FileRenameEventArgs(oldFilePath, FileSystemService.GetFilePathWithFolderName(newFilePath), newFilePath));
        }

        /// <summary>
        /// Saves all.
        /// </summary>
        public static void SaveAll()
        {
            OnSaveAllClicked(EventArgs.Empty);
        }

        /// <summary>
        /// Runs the project.
        /// </summary>
        public static void RunProject()
        {
			WatchLogFile(true);

            StartServer();
            
			if (!Options.Instance.RunServer)
			{
				LaunchBrowserIfNeeded();
				OnProjectStarted();
				return;
			}

        }

        private static void CreateWebServer()
        {
            if (_webServer == null)
            {
                _webServer = new WebServerProcess();
                _webServer.Started += new EventHandler(_webServer_Started);
                _webServer.OutputReceived += new EventHandler<DataReceivedEventArgs>(OutputReceivedHandler);

            }
        }

		private static LogFileWatcher logWatcher;

		private static void WatchLogFile(bool watch)
		{
			if (watch)
			{
				if (logWatcher == null)
				{
					logWatcher = new LogFileWatcher();
					logWatcher.ErrorFound += new EventHandler<LogFileWatcher.RailsLogFileEventArgs>(logWatcher_ErrorFound);
				}

				logWatcher.Start();
			}
			else if (logWatcher != null)
			{
				logWatcher.Stop();
			}
		}

		static void logWatcher_ErrorFound(object sender, LogFileWatcher.RailsLogFileEventArgs e)
		{
			if (LogErrorFound != null)
			{
				LogErrorFound(sender, e);
			}
		}

		private static void LaunchBrowserIfNeeded()
		{
            if (!Options.Instance.LaunchInternalBrowser)
            {
                int port = Options.Instance.ServerPort;
                if (_webServer != null)
                {
                    port = _webServer.Port;
                }
                System.Diagnostics.Process.Start(string.Format(Resources.LocalHostUri, port));
            }
		}

        /// <summary>
        /// Stops the project.
        /// </summary>
        public static void StopProject()
        {
			WatchLogFile(false);
            StopServer();
			OnProjectStopped();
        }

		/// <summary>
		/// Raises the ProjectStopped event.
		/// </summary>
		private static void OnProjectStopped()
		{
			_projectRunning = false;

			if (ProjectStopped != null)
			{
				ProjectStopped(null, EventArgs.Empty);
			}
		}

        /// <summary>
        /// Starts the server.
        /// </summary>
        public static void StartServer()
        {
			if (Options.Instance.RunServer)
			{
				CreateWebServer();

				_webServer.StartServer(_projectPath, Options.Instance.ServerPort);

				OnWebServerStarting(EventArgs.Empty);
			}
        }

        /// <summary>
        /// Stops the server.
        /// </summary>
        public static void StopServer()
        {
            if (_webServer != null)
            {
                _webServer.StopServer();
            }
        }

        /// <summary>
        /// Gets statistics for the project. 
        /// </summary>
        public static void GetProjectStats()
        {
            SetupRake(false);
            _rakeProc.GetProjectStats();
        }

        /// <summary>
        /// Gets statistics for the project. 
        /// </summary>
        public static void RunMigration(bool trace)
        {
            SetupRake(true);
            _rakeProc.RunMigration(trace);
        }

        /// <summary>
        /// Initializes the Rake process. 
        /// </summary>
        private static void SetupRake(bool standardOutput)
        {
            _rakeProc = new RakeProcess();
            _rakeProc.ErrorReceived += new EventHandler<DataReceivedEventArgs>(ErrorReceivedHandler);

            if (standardOutput)
            {
                _rakeProc.OutputReceived += new EventHandler<DataReceivedEventArgs>(OutputReceivedHandler);
                _rakeProc.Completed += new EventHandler(item_Completed);
            }
            else
            {
                _rakeProc.OutputReceived += new EventHandler<DataReceivedEventArgs>(_rakeProc_OutputReceived);
                _rakeProc.Completed += new EventHandler(_rakeProc_Completed);
            }

            _rakeProc.ProjectPath = _projectPath;
        }


        /// <summary>
        /// Renames the file.
        /// </summary>
        /// <param name="oldFilePath">The old file path.</param>
        /// <param name="newFileName">New name of the file.</param>
        /// <param name="newFilePath">The new file path.</param>
        public static void RenameFile(string oldFilePath, string newFileName, string newFilePath)
        {
            FileSystemService.RenameFile(oldFilePath, newFileName);

            RenameOpenFile(oldFilePath, newFilePath);

            OnFileRenamed(new FileRenameEventArgs(oldFilePath, FileSystemService.GetFilePathWithFolderName(newFilePath), newFilePath));

        }

        /// <summary>
        /// Changes the name of a file that was already open.
        /// This happens when a file is renamed by the user. 
        /// </summary>
        /// <param name="oldFilePath">The old file path.</param>
        /// <param name="newFileName">The new file path (oddly enough).</param>
        private static void RenameOpenFile(string oldFilePath, string newFileName)
        {
            // Only do this if the file is open, because the event OnFileRenamed is used
            // to rename file tabs and stuff. You understand.
            if (_openFiles.Contains(oldFilePath))
            {
                _openFiles.Remove(oldFilePath);
                _openFiles.Add(newFileName);
                
            }
        }

        /// <summary>
        /// Renames the directory.
        /// </summary>
        /// <param name="oldPath">The old path.</param>
        /// <param name="newPath">The new path.</param>
        public static void RenameDirectory(string oldPath, string newPath)
        {
            FileSystemService.RenameDirectory(oldPath, newPath);
        }

        /// <summary>
        /// Deletes the document.
        /// </summary>
        /// <param name="filePath">The file path.</param>
        public static void DeleteDocument(string filePath)
        {
            OnFileDeleted(new ProjectFileEventArgs(filePath));
        }

        /// <summary>
        /// Generates the new item.
        /// </summary>
        /// <param name="generatorType">Type of the generator.</param>
        /// <param name="arguments">The arguments.</param>
        public static void GenerateNewItem(string generatorType, string arguments, bool miscGenerator)
        {
            if (!Validator.ValidString(generatorType))
            {
                throw new ArgumentException();
            }

            if (miscGenerator)
            {
                generatorType = arguments.Substring(0, arguments.IndexOf(" "));
                arguments = arguments.Substring(arguments.IndexOf(" ") + 1);
            }
            GeneratorProcess generator = new GeneratorProcess();

            generator.OutputReceived += new EventHandler<DataReceivedEventArgs>(OutputReceivedHandler);
            generator.Completed += new EventHandler(item_Completed);
            generator.ErrorReceived += new EventHandler<DataReceivedEventArgs>(ErrorReceivedHandler);

            generator.Generate(generatorType, arguments);
        }

        /// <summary>
        /// Destroys the item.
        /// </summary>
        /// <param name="generatorType">Type of the generator.</param>
        /// <param name="arguments">The arguments.</param>
        public static void DestroyItem(string generatorType, string arguments, bool miscGenerator)
        {
            if (miscGenerator)
            {
                generatorType = arguments.Substring(0, arguments.IndexOf(" "));
                arguments = arguments.Substring(arguments.IndexOf(" ") + 1);
            }
            GeneratorProcess generator = new GeneratorProcess();

            generator.OutputReceived += new EventHandler<DataReceivedEventArgs>(OutputReceivedHandler);
            generator.Completed += new EventHandler(item_Completed);
            
            generator.Destroy(generatorType, arguments);

            // TODO: Must Call OnDelete so the tab can close.
        }

        public static void RemoveOpenFile(string filePath)
        {

            bool result = _openFiles.Remove(filePath);
            if (result)
            {
                OnFileDeleted(new ProjectFileEventArgs(filePath));
            }
        }

        /// <summary>
        /// Creates the project.
        /// </summary>
        /// <param name="path">The path.</param>
        public static void CreateProject(string path)
        {
            _newProject = new ProjectProcess();
            _newProject.Completed += new EventHandler(newProject_Completed);
            _newProject.OutputReceived += new EventHandler<DataReceivedEventArgs>(OutputReceivedHandler);

            _newProject.ProjectPath = path;
            _newProject.CreateProject();
        }

        /// <summary>
        /// Saves the recent projects.
        /// </summary>
        public static void SaveRecentProjects()
        {
            SerializationService.SerializeObject(Resources.RecentProjectsFile, _recentProjects);
        }

        /// <summary>
        /// Gets the recent projects.
        /// </summary>
        /// <returns></returns>
        public static IList<string> GetRecentProjects()
        {
            _recentProjects = (List<string>)SerializationService.DeserializeObject(Resources.RecentProjectsFile);

            return _recentProjects;
        }

        /// <summary>
        /// Updates the caret position.
        /// </summary>
        /// <param name="line">The line.</param>
        /// <param name="column">The column.</param>
        public static void UpdateCaretPosition(int line, int column)
        {
            // Zero based lines, columns.
            OnEditorCaretPositionChanged(new CaretPositionChangedEventArgs(line + 1, column + 1));
        }

        /// <summary>
        /// Updates the last document closed.
        /// </summary>
        public static void UpdateLastDocumentClosed()
        {
            OnLastDocumentClosed(EventArgs.Empty);
        }

        /// <summary>
        /// Adds the recent project.
        /// </summary>
        /// <param name="path">The path.</param>
        private static void AddRecentProject(string path)
        {
            if (_recentProjects == null)
            {
                _recentProjects = new List<string>();
            }

            // Add this as a recent project if it's not already there.
            // If it is, just ghost it, so it can be inserted at the top.
            if (_recentProjects.Contains(path))
            {
                _recentProjects.Remove(path);
            }

            // Makes sure our limit isn't reached, if so, deletes the last one.
            // We don't want to show like the last 8,000,000 projects opened
            // now do we??
            CheckRecentProjectCount();

            _recentProjects.Insert(0, path);

            OnRecentProjectAdded(new ProjectPathChangedEventArgs(string.Empty, path));
        }

        /// <summary>
        /// Checks the recent project count.
        /// </summary>
        private static void CheckRecentProjectCount()
        {
            if (_recentProjects.Count >= RecentProjectLimit)
            {
                _recentProjects.RemoveAt(_recentProjects.Count - 1);
            }
        }

        /// <summary>
        /// Runs all unit and functional tests for the project.
        /// </summary>
        /// <param name="trace">Should the full stack trace be displayed.</param>
        public static void RunAllTests(bool trace)
        {
            InitTesting();
            _testing.RunAllTests(trace);
        }
        
        /// <summary>
        /// Runs all unit tests for the project.
        /// </summary>
        /// <param name="trace">Should the full stack trace be displayed.</param>
        public static void RunAllUnitTests(bool trace)
        {
            InitTesting();
            _testing.RunAllUnitTests(trace);
        }

        /// <summary>
        /// Runs all functional tests for the project.
        /// </summary>
        /// <param name="trace">Should the full stack trace be displayed.</param>
        public static void RunAllFunctionalTests(bool trace)
        {
            InitTesting();
            _testing.RunAllFunctionalTests(trace);
        }

        /// <summary>
        /// Runs all integration tests for the project.
        /// </summary>
        /// <param name="trace">Should the full stack trace be displayed.</param>
        public static void RunAllIntegrationTests(bool trace)
        {
            InitTesting();
            _testing.RunAllIntegrationTests(trace);
        }

        /// <summary>
        /// Runs just the tests for a given file.
        /// </summary>
        /// <param name="path">Path to the file.</param>
        public static void RunTestForFile(string path)
        {
            if (!Validator.ValidFile(path))
            {
                throw new ArgumentException(Resources.FileDoesNotExist);
            }

            InitTesting();
            _testing.RunTestsForFile(path);
        }

        /// <summary>
        /// Runs just the tests for a given directory.
        /// </summary>
        /// <param name="path">Path to the directory.</param>
        public static void RunTestsForDirectory(string path, bool trace)
        {
            if (!Validator.ValidDirectory(path))
            {
                throw new ArgumentException(Resources.DirectoryDoesNotExist);
            }

            InitTesting();
            _testing.RunTestsForDirectory(path, trace);
        }

        /// <summary>
        /// Sets up the testing object.
        /// </summary>
        private static void InitTesting()
        {
            if (_testing == null)
            {
                _testing = new Testing();
                _testing.OutputReceived += new EventHandler<DataReceivedEventArgs>(OutputReceivedHandler);
                _testing.Completed += new EventHandler(item_Completed);
                _testing.ErrorReceived += new EventHandler<DataReceivedEventArgs>(ErrorReceivedHandler);
            }
            _testing.ProjectPath = _projectPath;
        }

        /// <summary>
        /// Used to fire the SelectedFileChanged event.
        /// </summary>
        /// <param name="newFilePath"></param>
        public static void ChangeSelectedFile(string newFilePath)
        {
            SelectedFileChangedEventArgs e = null;

            if (newFilePath.Trim().Length != 0)
            {
                FileInfo fileInfo = new FileInfo(newFilePath);

                e = new SelectedFileChangedEventArgs(newFilePath, fileInfo.Length.ToString(),
                    fileInfo.LastAccessTime, fileInfo.LastWriteTime, fileInfo.CreationTime, fileInfo.Name);
            }

            OnSelectedFileChanged(e);
            
        }

        /// <summary>
        /// Used to fire the TabChanged event.
        /// </summary>
        /// <param name="newTabFilePath"></param>
        public static void ChangeTab(string newTabFilePath)
        {
            OnTabChanged(new ProjectFileEventArgs(newTabFilePath));
        }

        /// <summary>
        /// Modifies the dispatch.rb file for either
        /// Unix or Windows.
        /// </summary>
        /// <param name="unix"></param>
        public static void PrepareForServer(bool unix)
        {
            if (unix)
            {
                FileSystemService.PrepareForUnix(_projectPath);
            }
            else
            {
                FileSystemService.PrepareForWindows(_projectPath);
            }
            OnPreparedForSever(new ProjectFileEventArgs(_projectPath + Resources.PathToDispatchFile));
        }

        /// <summary>
        /// Used to fire the ProjectLoaded event.
        /// </summary>
        public static void NotifyProjectLoaded(string path)
        {
            OnProjectLoaded(new ProjectLoadedEventArgs(path));
        }

        /// <summary>
        /// Outputs the received handler.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.Diagnostics.DataReceivedEventArgs"/> instance containing the event data.</param>
        private static void OutputReceivedHandler(object sender, DataReceivedEventArgs e)
        {
            OnOutputReceived(e);
        }

        /// <summary>
        /// Handles the Completed event of the newProject control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private static void newProject_Completed(object sender, EventArgs e)
        {
            CloseCurrentProject();

            ProjectPath = _newProject.ProjectPath;
        }

        /// <summary>
        /// Handles the Completed event of the generator control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private static void item_Completed(object sender, EventArgs e)
        {
            OnTaskCompleted(e);
        }

        /// <summary>
        /// Handles the Started event of the _webServer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private static void _webServer_Started(object sender, EventArgs e)
        {
            if (!_projectRunning)
            {
				LaunchBrowserIfNeeded();
				OnWebServerStarted(e);
            }

			OnProjectStarted();
        }

        /// <summary>
        /// Handles the Completed event of the _rakePrco object.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void _rakeProc_Completed(object sender, EventArgs e)
        {
            OnRakeOutputRecived(new RakeEventArgs(_rakeOutput));
            // Just in case it gets run multiple times.
            _rakeOutput = string.Empty;
        }

        /// <summary>
        /// Handles the OutputRecived event for the _rakeProc object.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void _rakeProc_OutputReceived(object sender, DataReceivedEventArgs e)
        {
            if (!string.IsNullOrEmpty(e.Data))
            {
                _rakeOutput += e.Data + "\r";
            }
        }

        private static void ErrorReceivedHandler(object sender, DataReceivedEventArgs e)
        {
            OnErrorRecieved(e);
        }


    }
}
